/*
 * Copyright (c) 2012 Johan Fylling
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 */

part of engine;


typedef void WorldObjectAttributeListener (var data);


class WorldObjectAttribute {
  var                          _data;
  WorldObjectAttributeListener _listener;
  
  WorldObjectAttribute ({var data, WorldObjectAttributeListener listener}) {
    _data = data;
    _listener = listener;
  }
  
  void set data (var data) { 
    _data = data;
    if (_listener != null) {
      _listener(data);
    }
  }
  
  toJson () {
    return {
      "unknown": _data
    };
  }
}


class BoolWorldObjectAttribute extends WorldObjectAttribute {
  BoolWorldObjectAttribute ({bool data, WorldObjectAttributeListener listener}) : super(data:data, listener:listener);
  
  bool get data => _data;
  
  toJson () {
    return {
      "bool": _data
    };
  }
}


class NumWorldObjectAttribute extends WorldObjectAttribute {
  NumWorldObjectAttribute ({num data, WorldObjectAttributeListener listener}) : super(data:data, listener:listener);
  
  num get data => _data;
  
  toJson () {
    return {
      "num": _data
    };
  }
}


class StringWorldObjectAttribute extends WorldObjectAttribute {
  StringWorldObjectAttribute ({String data, WorldObjectAttributeListener listener}) : super(data:data, listener:listener);
  
  String get data => _data;
  
  toJson () {
    return {
      "string": _data
    };
  }
}


class WorldObject extends SmartElement {
  static int                              _idBase    = 1;
  int                                     _id        = 0;
  
  DynamicBody                             _body;
  final Map<String, WorldObjectAttribute> attributes = new Map<String, WorldObjectAttribute>();
  final Inventory                         inventory  = new Inventory();
  
  WorldObject ([DynamicBody body]) : _id = ++_idBase {
    this.body = (?body ? body : new DynamicBody());
  }
  
  int get id => this._id;
  
  DynamicBody get body => this._body;
  
  void set body (DynamicBody body) {
    this._body = body;
    this._body.data = this;
  }
  
  String toString () => "wo[${this._id}"
    "${this.body != null ? ",${this.body}" : ""}]";
  
  toJson () {
    var data = {
      "type": "world_object",
      "id": _id,
      "body": _body
    };
    if (!attributes.isEmpty) {
      data["attributes"] = attributes;
    }
    if (!inventory.isEmpty) {
      data["inventory"] = inventory;
    }
    return data;
  }
}


class WorldObjectStorageHandler {
  call (StorageManager manager, Map attributes) {
    WorldObject wo = new WorldObject();
    setAttributes(manager, attributes, wo);
    return wo;
  }
  
  static void setAttributes (StorageManager manager, Map attributes, WorldObject wo) {
    var bodyAttributes = attributes["body"];
    assert(bodyAttributes is Map);
    var body = manager.load(bodyAttributes);
    assert(body is DynamicBody);
    wo.body = body;
    
    var inventoryList = attributes["inventory"];
    if (inventoryList != null) {
      assert(inventoryList is List);
      InventoryStorageHandler.setup(manager, inventoryList, wo.inventory);
    }
    
    var attributesAttributes = attributes["attributes"];
    if (attributesAttributes != null) {
      assert(attributesAttributes is Map);
      setupAttributes (manager, attributesAttributes, wo.attributes);
    }
  }
  
  static void setupAttributes (StorageManager manager, Map data, Map<String, WorldObjectAttribute> attributes) {
    data.forEach((var key, var value) {
      assert(key is String);
      assert(value is Map);
      assert(value.length == 1);
      if (value.containsKey("bool")) {
        attributes[key] = new BoolWorldObjectAttribute(data:value["bool"]);
      } else if (value.containsKey("num")) {
        attributes[key] = new NumWorldObjectAttribute(data:value["num"]);
      } else if (value.containsKey("string")) {
        attributes[key] = new StringWorldObjectAttribute(data:value["string"]);
      } else {
        print("Unknown attribute type: ${value.keys.first}");
      }
    });
  }
}
